package com.ruoyi.cmdb.util;

import com.ruoyi.cmdb.vo.IpNetwork;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * Ip工具类
 *
 * @author ggc
 */
public class Ip {
    //获取本地的公网ip
    public String findClosestInternalIp() {
        try {

            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            //System.out.println(interfaces);
            for (NetworkInterface intf : interfaces) {
                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
                for (InetAddress addr : addrs) {
                    if (!addr.isLoopbackAddress() && addr instanceof Inet4Address) {
                        String ip = addr.getHostAddress();
                        if (isPublicIp(ip)) {
                            // 这是一个公网IP，直接返回
                            return ip;
                        } else {
                            // 这是一个内网IP，保存下来以便后面比较
                            // 这里你可能需要添加更多的逻辑来比较哪个内网IP最接近公网
                        }
                    }
                }
            }
        } catch (SocketException ex) {
            ex.printStackTrace();
        }
        return null;  // 没有找到公网IP或内网IP
    }
    //判断ip是不是公网ip
    @SneakyThrows
    public boolean isPublicIp(String ip) {
        // 这里可以使用外部服务来验证IP是否是公网IP
        // 例如，你可以发送一个HTTP请求到一个公开的IP信息API，并检查返回的响应
        // 这里为了简化，我们仅基于IP地址范围进行判断
        // 注意：这种方法可能不准确，因为IP地址范围可能会变化
        String[] publicIpRanges = {
                "64.0.0.0-127.255.255.255",
                "128.0.0.0-191.255.255.255",
                // 添加更多公网IP地址范围...
        };
        for (String range : publicIpRanges) {
            String[] parts = range.split("-");
            //if (ipInRange(ip, parts[0], parts[1])) {
            if (isBetween(ip, parts[0], parts[1])) {
                return true;
            }
        }
        return false;
    }

    //获取本地内网ip
    public List<String> findLocalIp() {
        try {

            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
            //System.out.println(interfaces);
            List<String> privateIps = new ArrayList<>();
            List<String> publicIps = new ArrayList<>();
            for (NetworkInterface intf : interfaces) {
                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
                for (InetAddress addr : addrs) {
                    if (!addr.isLoopbackAddress() && addr instanceof Inet4Address) {
                        String ip = addr.getHostAddress();
                        if (isPrivateIp(ip)) {
                            // 这是一个内网IP，
                            //System.out.println("privateIp:" + ip);
                            privateIps.add(ip);
                        } else {
                            // 这是一个公网IP，保存下来以便后面比较
                            publicIps.add(ip);
                            //System.out.println("publicIp:" + ip);
                        }
                    }
                }
            }
            return privateIps;
        } catch (SocketException ex) {
            ex.printStackTrace();
        }
        return null;  // 没有找到公网IP或内网IP
    }
    //判断ip是不是内网ip
    @SneakyThrows
    public boolean isPrivateIp(String ip) {
        String[] privateIpRanges = {
                "10.0.0.0-10.255.255.255",
                "172.16.0.0-172.31.255.255",
                "192.168.0.0-192.168.255.255"
                // 添加更多内网IP地址范围...
        };
        for (String range : privateIpRanges) {
            String[] parts = range.split("-");
            //if (ipInRange(ip, parts[0], parts[1])) {
            if (isBetween(ip, parts[0], parts[1])) {
                return true;
            }
        }
        return false;
    }

    public List<String> traceroute(String destination) {
        return null;
    }

    //根据给的开始ip和结束ip,得到区间ip段,包含开头和结束ip
    @SneakyThrows
    public List<String> getIpArray(String start,String end) {
//        String startIp="192.168.1.1";
//        String stopIp="192.168.2.10";

        try  {
            // 定义起始和结束IP地址的整数形式
            int startIp = ipToInt(start);
            int endIp = ipToInt(end);

            // 创建一个列表来存储IP地址
            List<String> ipAddresses = new ArrayList<>();

            // 遍历IP地址范围
            for (int i = startIp; i <= endIp; i++) {
                // 将整数形式的IP地址转换回点分十进制表示法
                String ip = intToIp(i);

                // 检查转换后的IP地址是否在原始范围内（考虑子网掩码）
                if (isBetween(ip, start, end)) {
                    //System.out.println(ip);
                    //System.out.println(start);
                    //System.out.println(end);
                    //System.out.println(isBetween(ip, start, end));
                    ipAddresses.add(intToIp(i));
                }
            }

            // 打印IP地址的整数形式
            //for (int ip : ipAddresses) {
            //    System.out.println(intToIp(ip));
            //}
            return ipAddresses;

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 将点分十进制的IP地址转换为整数形式
    private static int ipToInt(String ip) throws UnknownHostException {
        byte[] bytes = InetAddress.getByName(ip).getAddress();
        return ((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) | ((bytes[2] & 0xff) << 8) | (bytes[3] & 0xff);// 这个算法看不懂,那就不用看了.会用就行
    }
//    在Java中，(bytes[0] & 0xff) << 24 这个表达式涉及到了位运算和类型转换。让我们一步一步地拆解这个表达式：
//    bytes[0]: 这表示bytes数组的第一个元素。bytes是一个字节数组（byte[]），其中每个元素是一个8位的字节，其值范围从-128到127（在Java中，byte是有符号的）。
//            & 0xff: 这是一个按位与（AND）操作。0xff是十六进制的表示方式，等于二进制的11111111。这个操作确保我们只保留bytes[0]的低8位，并且丢弃任何高位。由于bytes[0]本身就是一个8位的字节，这个操作实际上不会改变它的值，但是明确了这个操作可以帮助避免某些类型转换时的错误。
//            << 24: 这是一个左移（Left Shift）操作。它将上一步得到的结果向左移动24位。在二进制中，这相当于在结果的左边添加了24个0。由于我们是从一个8位的字节开始，左移24位之后，这个字节现在被放置在了一个32位整数的最高8位上，其余24位都是0。
//    举个例子，如果bytes[0]的值是0x01（在十进制中是1），那么bytes[0] & 0xff的结果仍然是0x01。左移24位后，结果变为0x01000000，在十进制中等于16,777,216。
//    这个操作通常用于将IP地址的各个部分（每个部分是一个字节）组合成一个32位的整数。IP地址由四个字节组成，每个字节表示一个0到255之间的数。通过将每个字节左移相应的位数（第一个字节左移24位，第二个字节左移16位，以此类推），我们可以将这些字节组合成一个整数。

    // 将整数形式的IP地址转换为点分十进制表示法
    private static String intToIp(int ip) {
        return ((ip >> 24) & 0xFF) + "." +
                ((ip >> 16) & 0xFF) + "." +
                ((ip >> 8) & 0xFF) + "." +
                (ip & 0xFF);
    }

    // 检查IP地址是否在指定的范围内
    @SneakyThrows
    public static boolean isBetween(String ip, String start, String end) throws UnknownHostException {
        int ipInt = ipToInt(ip);
        int startInt = ipToInt(start);
        int endInt = ipToInt(end);
        //System.out.println(ipInt);
        //System.out.println(startInt);
        //System.out.println(endInt);
        return ipInt >= startInt && ipInt <= endInt;
    }

    //生成一个根据任意一个ip和掩码得出全部广播地址,网络段的方法
    @SneakyThrows
    public IpNetwork getNetworkRange(String ip, String netmask) {
        //String ipAddress = "192.168.1.2";
        //String subnetMask = "255.255.255.192";

        // 将IP地址和子网掩码转换为字节数组
        byte[] ipBytes = InetAddress.getByName(ip).getAddress();
        byte[] maskBytes = InetAddress.getByName(netmask).getAddress();

        // 计算网络地址
        byte[] networkBytes = new byte[4];
        for (int i = 0; i < 4; i++) {
            networkBytes[i] = (byte) (ipBytes[i] & maskBytes[i]);
        }

        // 将网络地址转换回字符串形式
        String networkAddress = bytesToIp(networkBytes);

        // 计算广播地址
        byte[] broadcastBytes = new byte[4];
        for (int i = 0; i < 4; i++) {
            // 将子网掩码中的0位全部置为1
            byte inverseMaskByte = (byte) ~maskBytes[i];
            // 对网络地址的主机部分应用取反的子网掩码
            broadcastBytes[i] = (byte) (networkBytes[i] | inverseMaskByte);
        }

        // 将广播地址转换回字符串形式
        String broadcastAddress = bytesToIp(broadcastBytes);

        return new IpNetwork(networkAddress,broadcastAddress,netmask);
        // 打印网段范围
        //System.out.println("Network Address: " + networkAddress);
        //System.out.println("Broadcast Address: " + broadcastAddress);
        //System.out.println("Subnet Range: " + networkAddress + " - " + broadcastAddress);
        //return null;
    }

    // 将字节数组转换为点分十进制的IP地址
    private static String bytesToIp(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(b & 0xFF).append(".");
        }
        // 移除最后的点号
        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    //检测ip状态
    @SneakyThrows
    public boolean ipStatus(String ipAddress){
        int TIMEOUT = 5000; // 超时时间，单位毫秒
        InetAddress inetAddress = InetAddress.getByName(ipAddress);
        String result="";
        boolean b;
        if (inetAddress.isReachable(TIMEOUT)) {
            result= ipAddress + " is up";
            b=true;
        } else {
            result= ipAddress + " is down";
            b=false;
        }
        System.out.println(result);
        return b;
    }

    //获取到达ip的时间方法,
    @SneakyThrows
    public String pingIp(String ipAddress) throws IOException {
        // 构建ping命令
        String pingCommand = "ping -c 1 " + ipAddress;
        // 执行ping命令并获取输出
        Process process = Runtime.getRuntime().exec(pingCommand);
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        int exitCode = process.waitFor();
        // 检查命令执行结果
        String result="";
        if (exitCode == 0) {
            String line;
            while ((line = reader.readLine()) != null) {
                if(line.contains("rtt")){
                    result= line.split("/")[5];
                    //System.out.println("111111111");
                    //System.out.println(line);
                    //System.out.println(String.join(",",line.split("/")));
                    return result;
                }else{
                    //System.out.println(line);
                    result="no rtt";
                };
            }
        } else {
            result= "exitCode:"+exitCode;
        }
        //System.out.println("response:"+result);
        return result;
    }

    //获取本地路由器的外网ip
    public String getInternetIp(){
        RestTemplate restTemplate=new RestTemplate();
        String internetIp = restTemplate.getForObject("http://ifconfig.me", String.class);
        //System.out.println(internetIp);
        return internetIp;
    }

}